package com.gezi.admin.config.redis;

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * @Author 格子代码
 * @createDate 2024/2/10 09:00
 * @contact 1424529270@qq.com
 */
@Component
public class RedisUtil {

    public static RedisTemplate<String, Object> redisTemplate;

    @Resource
    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        RedisUtil.redisTemplate = redisTemplate;
    }

    /**
     * key是否存在
     *
     * @param key
     * @return
     */
    public static Boolean keyExist(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 删除key
     *
     * @param key
     * @return
     */
    public static Boolean deleteKey(String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 批量删除key
     */
    public static void deleteKeys(String pattern) {
        List<String> keyList = getScanKeys(pattern);
        for (String key : keyList) {
            deleteKey(key);
        }
    }

    /**
     * scan 实现
     *
     * @param pattern  表达式
     * @param consumer 对迭代到的key进行操作
     */
    public static void scanKeys(String pattern, Consumer<byte[]> consumer) {
//        redisTemplate.execute((RedisConnection connection) -> {
//            try (Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().count(Long.MAX_VALUE).match(pattern).build())) {
//                cursor.forEachRemaining(consumer);
//                return null;
//            }
//        });
    }

    /**
     * 获取符合条件的key
     *
     * @param pattern 表达式
     * @return
     */
    public static List<String> getScanKeys(String pattern) {
        List<String> keys = new ArrayList<>();
        scanKeys(pattern, item -> {
            //符合条件的key
            String key = new String(item, StandardCharsets.UTF_8);
            keys.add(key);
        });
        return keys;
    }

    /**
     * 获取符合条件的key
     *
     * @param pattern 表达式
     * @return
     */
    public static Set<String> getKeys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 设置key过期时间
     *
     * @param key
     * @param time
     * @param unit
     * @return
     */
    public static Boolean setKeyExp(String key, Long time, TimeUnit unit) {
        return redisTemplate.expire(key, time, unit);
    }

    /**
     * 获取key过期时间
     *
     * @param key
     * @param unit
     * @return
     */
    public static Long getKeyExp(String key, TimeUnit unit) {
        return redisTemplate.getExpire(key, unit);
    }


    /**
     * 普通存储
     *
     * @param key
     * @param value
     */
    public static void setNormalKey(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 时效存储
     *
     * @param key
     * @param value
     * @param time
     * @param unit
     */
    public static void setTimeNormalKey(String key, Object value, Long time, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, time, unit);
    }

    /**
     * 批量存储 缓存中有的key会被覆盖
     * <p>
     * todo 未用过
     *
     * @param map
     */
    public static void setBatchString(Map<String, Object> map) {
        redisTemplate.opsForValue().multiSet(map);
    }

    /**
     * 批量存储 缓存中有的会跳过
     * todo 未用过
     *
     * @param map
     */
    public static void setBatchIfAbsent(Map<String, Object> map) {
        redisTemplate.opsForValue().multiSetIfAbsent(map);
    }


    /**
     * 对value进行加减
     * 如果该 key 不存在 将创建一个key 并赋值该 number
     * 如果 key 存在,但 value 不是长整型 ,将报错
     * 返回计算后的值
     *
     * @param key
     * @param number
     * @return
     */
    public static Long increment(String key, long number) {
        return redisTemplate.opsForValue().increment(key, number);
    }

    /**
     * 根据批量key获取值
     *
     * @param keys
     * @return
     */
    public static List<Object> multiGetValue(List<String> keys) {
        return redisTemplate.opsForValue().multiGet(keys);
    }

    /**
     * 根据key获取value
     *
     * @param key
     * @return
     */
    public static <T>T getNormalValue(String key) {
        ValueOperations<String, T> operation = (ValueOperations<String, T>) redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 向set集合添加
     *
     * @param key
     * @param value
     */
    public static void pushSet(String key, Object... value) {
        redisTemplate.opsForSet().add(key, value);
    }

    /**
     * set集合删除值
     *
     * @param key
     * @param value
     */
    public static void setRemoveValue(String key, Object value) {
        redisTemplate.opsForSet().remove(key, value);
    }

    /**
     * 根据key获取value值
     *
     * @param key
     */
    public static Set<Object> getSetValue(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 随机获取指定数量set中的值
     *
     * @param key
     * @param count
     */
    public static List<Object> getSetRandomValues(String key, long count) {
        return redisTemplate.opsForSet().randomMembers(key, count);
    }

    /**
     * 随机获取set中的值
     *
     * @param key
     * @return
     */
    public static Object getSetRandomValue(String key) {
        return redisTemplate.opsForSet().randomMember(key);
    }

    /**
     * 随机弹出set中的一个value 并且返回这个value
     *
     * @param key
     * @return
     */
    public static Object popSetReturnValue(String key) {
        return redisTemplate.opsForSet().pop(key);
    }

    /**
     * 获取set中value的个数
     *
     * @param key
     * @return
     */
    public static Long getSetSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    /**
     * 判断value是否存在set中
     *
     * @param key
     * @param value
     * @return
     */
    public static Boolean valueExistSet(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 移动set中的value
     * todo 未用过
     *
     * @param key
     * @param destKey
     * @param value
     */
    public static Boolean moveSetValue(String key, String destKey, Object value) {
        return redisTemplate.opsForSet().move(key, value, destKey);
    }

    /**
     * key 与destKey的差集
     * todo 未用过
     *
     * @param key     键
     * @param destKey 键
     * @return
     */
    public static Set<Object> getSetDifference(String key, String destKey) {
        return redisTemplate.opsForSet().difference(key, destKey);
    }

    /**
     * key与 destKey的交集
     * todo 未用过
     *
     * @param key
     * @param destKey
     * @return
     */
    public static Set<Object> getSetIntersect(String key, String destKey) {
        return redisTemplate.opsForSet().intersect(key, destKey);
    }

    /**
     * key与 destKey的并集
     * todo 未用过
     *
     * @param key
     * @param destKey
     * @return
     */
    public static Set<Object> getSetUnion(String key, String destKey) {
        return redisTemplate.opsForSet().union(key, destKey);
    }

    /**
     * hash 添加值
     *
     * @param key
     * @param hashKey
     * @param value
     */
    public static void pushHash(String key, Object hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    /**
     * hash 添加值 如果存在不添加
     *
     * @param key
     * @param hashKey
     * @param value
     */
    public static void pushIfHash(String key, Object hashKey, Object value) {
        redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
    }


    /**
     * 批量添加
     * hash key value
     *
     * @param key
     * @param map
     */
    public static void pushAllHash(String key, Map<Object, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 根据key 获取hash 键值对
     *
     * @param key
     * @return
     */
    public static Map<Object, Object> getHashValue(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 判断hashKey是否存在
     *
     * @param key
     * @param hashKey
     * @return
     */
    public static Boolean hashKeyExist(String key, Object hashKey) {
        return redisTemplate.opsForHash().hasKey(key, hashKey);
    }

    /**
     * 根据key hash keu获取value值
     *
     * @param key
     * @param hashKey
     * @return
     */
    public static Object getHashKeyValue(String key, Object hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    /**
     * 删除hashKey
     *
     * @param key
     * @param hashKey
     */
    public static void deleteHashKey(String key, Object... hashKey) {
        redisTemplate.opsForHash().delete(key, hashKey);
    }


    /**
     * 给指定 hash 的 hash key 做增减操作
     *
     * @param key
     * @param hashKey
     * @param number
     * @return
     */
    public static Long hashIncrement(String key, Object hashKey, long number) {
        return redisTemplate.opsForHash().increment(key, hashKey, number);
    }

    /**
     * 获取key下的hashKey
     *
     * @param key
     * @return
     */
    public static Set<Object> getHashKeys(String key) {
        return redisTemplate.opsForHash().keys(key);
    }

    /**
     * 获取指定 hash 下面的 键值对 数量
     *
     * @param key
     * @return
     */
    public static Long getHashSize(String key) {
        return redisTemplate.opsForHash().size(key);
    }

    /**
     * 集合左添加
     *
     * @param key
     * @param value
     */
    public static void leftPush(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 把最后一个参数值放到指定集合的第一个出现中间参数的前面，如果中间参数值存在的话。
     *
     * @param key
     * @param pivot
     * @param value
     */
    public static void leftJoinPush(String key, Object pivot, Object value) {
        redisTemplate.opsForList().leftPush(key, pivot, value);
    }

    /**
     * 集合右添加
     *
     * @param key
     * @param value
     */
    public static void rightPush(String key, Object value) {
        redisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * 把最后一个参数值放到指定集合的第一个出现中间参数的前面，如果中间参数值存在的话。
     *
     * @param key
     * @param pivot
     * @param value
     */
    public static void rightJoinPush(String key, Object pivot, Object value) {
        redisTemplate.opsForList().rightPush(key, pivot, value);
    }

    /**
     * 从右边弹出
     *
     * @param key
     * @return
     */
    public static Object popRight(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }


    /**
     * 从左边弹出
     *
     * @param key
     * @return
     */
    public static Object popLeft(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 获取指定位置元素 0是开头, -1是结尾
     *
     * @param key
     * @param index
     * @return
     */
    public static Object getListIndex(String key, Long index) {
        return redisTemplate.opsForList().index(key, index);
    }

    /**
     * 区间取值
     *
     * @param key
     * @param start 0 开始
     * @param end   -1  结尾
     * @return
     */
    public static List<Object> getRangeList(String key, Long start, Long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 向左边批量添加参数元素。
     *
     * @param key
     * @param values
     * @return
     */
    public static void leftPushAll(String key, Object... values) {
        redisTemplate.opsForList().leftPushAll(key, values);
    }

    /**
     * 向右边批量添加参数元素。
     *
     * @param key
     * @param values
     * @return
     */
    public static void rightPushAll(String key, Object... values) {
        redisTemplate.opsForList().rightPushAll(key, values);
    }

    /**
     * 获取集合长度
     *
     * @param key
     * @return
     */
    public static long getListLength(String key) {
        return redisTemplate.opsForList().size(key);
    }

    /**
     * 移除集合中右边的元素在等待的时间里，如果超过等待的时间仍没有元素则退出。
     * 阻塞弹出
     *
     * @param key
     * @return
     */
    public static Object rightBlockPop(String key, long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().rightPop(key, timeout, unit);
    }

    /**
     * 移除集合中左边的元素在等待的时间里，如果超过等待的时间仍没有元素则退出。
     * 阻塞弹出
     *
     * @param key
     * @return
     */
    public static Object leftBlockPop(String key, long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().leftPop(key, timeout, unit);
    }

    /**
     * zset存储
     *
     * @param key
     * @param value
     * @param score 用来排序
     */
    public static Boolean pushZSet(String key, Object value, double score) {
        Boolean flag = redisTemplate.opsForZSet().add(key, value, score);
        return flag;
    }

    /**
     * zSet 删除
     *
     * @param key
     * @param obj
     */
    public static Long zSetRemove(String key, Object obj) {
        return redisTemplate.opsForZSet().remove(key, obj);
    }

    /**
     * 获取值得分数
     *
     * @param key
     * @param value
     * @return
     */
    public static Double getZeSetValueScore(String key, Object value) {
        Double score = redisTemplate.opsForZSet().score(key, value);
        return score;
    }

    /**
     * 根据score获取区间值
     *
     * @param key
     * @param scoreA
     * @param scoreB
     * @return
     */
    public static Set<Object> zSetRangeByScore(String key, double scoreA, double scoreB) {
        return redisTemplate.opsForZSet().rangeByScore(key, scoreA, scoreB);
    }

    /**
     * 根据score获取区间值
     *
     * @param key
     * @param scoreA min
     * @param scoreB max
     * @return
     */
    public static Set<Object> zSetReverseRangeByScore(String key, double scoreA, double scoreB) {
        return redisTemplate.opsForZSet().reverseRangeByScore(key, scoreA, scoreB);
    }

    /**
     * 根据score移除区间值
     *
     * @param key
     * @param scoreA min
     * @param scoreB max
     */
    public static void removeRangeByScore(String key, double scoreA, double scoreB) {
        redisTemplate.opsForZSet().removeRangeByScore(key, scoreA, scoreB);
    }

    /**
     * 获取区间的元素个数
     *
     * @param key
     * @param scoreA
     * @param scoreB
     * @return
     */
    public static Long getZSetScoreCount(String key, double scoreA, double scoreB) {
        return redisTemplate.opsForZSet().count(key, scoreA, scoreB);
    }

    /**
     * 获取变量中元素的索引,下标开始位置为0
     *
     * @param key
     * @param object
     * @return
     */
    public static Long zSetRank(String key, Object object) {
        return redisTemplate.opsForZSet().rank(key, object);
    }

    /**
     * 获取指定区间元素
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public static Set<Object> getZSet(String key, Long start, Long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    /**
     * 获取指定区间元素 倒序获取
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public static Set<Object> getZSetReverse(String key, Long start, Long end) {
        return redisTemplate.opsForZSet().reverseRange(key, start, end);
    }

    /**
     * 删除zSet值
     *
     * @param key
     * @param obj
     * @return
     */
    public static boolean removeZSet(String key, Object... obj) {
        Long removeCount = redisTemplate.opsForZSet().remove(key, obj);
        if (Objects.isNull(removeCount) || removeCount <= 0)
            return false;
        return true;
    }

    /**
     * 发送消息
     *
     * @param topic
     * @param message
     */
    public static void sendPubMessage(String topic, String message) {
        redisTemplate.convertAndSend(topic, message);
    }


    public static Boolean mark(String key, long offset, boolean tag) {
        return redisTemplate.opsForValue().setBit(key, offset, tag);
    }

    public static Boolean container(String key, long offset) {
        return redisTemplate.opsForValue().getBit(key, offset);
    }

    /**
     * 获取bitmap的key值总和
     * @param key
     * @return
     */
    public static long bitCount(String key) {
        return redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
                return redisConnection.bitCount(key.getBytes());
            }
        });
    }

    /**
     * 获取bitmap的key值总和
     * @param key
     * @param start
     * @param end
     * @return
     */
    public static long bitCount(String key,long start, long end) {
        return redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
                return redisConnection.bitCount(key.getBytes(),start,end);
            }
        });
    }
}
